const hx = require('hbuilderx')
const _ = require('lodash')
const viewManager = require('./view-manager.js')
const ssh = require('./ssh.js')
const cache = require('./cache.js')
const os = require('../utils/os.js')
const log = require('../utils/log.js')

const { format } = require('util')
const { forward } = require('./mapping-port.js')
const { requireObject } = require('process-worker-executor')
const { join } = require('path')

function setShell(opts) {
    const statusBar = hx.window.createStatusBarItem(hx.StatusBarAlignment.Right, 100)
    const command = format('extension.%s.%s', opts.host, 'shell')
    const disposable = hx.commands.registerCommand(command, async () => {
        await viewManager.open('shell', opts.host, opts)
    })
    opts.statusBarShell = statusBar
    opts.disposeShellCommand = disposable
    cache.constant.context.subscriptions.push(disposable)
    statusBar.text = format('%s-%s', opts.name || opts.host, '终端')
    statusBar.command = command
    statusBar.tooltip = format('%s\n服务器终端', opts.host)
    statusBar.show()
}

function setFileExplorer(opts) {
    const statusBar = hx.window.createStatusBarItem(hx.StatusBarAlignment.Right, 100)
    const command = format('extension.%s.%s', opts.host, 'explorer')
    const disposable = hx.commands.registerCommand(command, () => {
        viewManager.open('server-explorer', opts.host, opts)
    })
    hx.commands.executeCommand(command)
    opts.statusBarExplorer = statusBar
    opts.disposeExplorerCommand = disposable
    cache.constant.context.subscriptions.push(disposable)
    statusBar.text = format('%s-%s', opts.name || opts.host, '文件')
    statusBar.command = command
    statusBar.tooltip = format('%s\n服务器文件资源管理器', opts.host)
    statusBar.show()
}

function setPortForward(opts) {
    const statusBar = hx.window.createStatusBarItem(hx.StatusBarAlignment.Right, 100)
    const command = format('extension.%s.%s', opts.host, 'forward')
    const disposable = hx.commands.registerCommand(command, () => {
        viewManager.open('port-forward', opts.host, opts)
    })
    opts.statusBarForward = statusBar
    opts.disposeForwardCommand = disposable
    cache.constant.context.subscriptions.push(disposable)
    statusBar.text = format('%s-%s', opts.name || opts.host, '端口')
    statusBar.command = command
    statusBar.tooltip = format('%s\n端口转发', opts.host)
    statusBar.show()
}

function setStats(opts) {
    const statusBar = hx.window.createStatusBarItem(hx.StatusBarAlignment.Right, 100)
    const command = format('extension.%s.%s', opts.host, 'stats')
    const disposable = hx.commands.registerCommand(command, () => {
        viewManager.open('server-stats', opts.host, opts)
    })
    opts.statusBarForward = statusBar
    opts.disposeForwardCommand = disposable
    cache.constant.context.subscriptions.push(disposable)
    statusBar.text = format('%s-%s', opts.name || opts.host, '状态')
    statusBar.command = command
    statusBar.tooltip = format('%s\n服务器状态', opts.host)
    statusBar.show()
}
async function save(opts) {
    await cache.clients.save(opts)
}

function setClose(opts) {
    const statusBar = hx.window.createStatusBarItem(hx.StatusBarAlignment.Right, 100)
    const command = format('extension.%s.%s', opts.host, 'close')
    const disposable = hx.commands.registerCommand(command, async () => {
        disconnect(opts.host)
    })
    opts.statusBarClose = statusBar
    opts.disposeCloseCommand = disposable
    cache.constant.context.subscriptions.push(disposable)
    statusBar.text = format('%s-%s', opts.name || opts.host, '关闭')
    statusBar.command = command
    statusBar.tooltip = format('%s\n关闭服务器连接并清理资源', opts.host)
    statusBar.show()
}

function setReconnect(opts) {
    if (opts.statusBarReconnect) {
        return
    }
    const statusBar = hx.window.createStatusBarItem(hx.StatusBarAlignment.Right, 100)
    const command = format('extension.%s.%s', opts.host, 'reconnect')
    const disposable = hx.commands.registerCommand(command, async () => {
        const conn = cache.connections.get(opts.host).connection
        try {
            log.notice.info('正在重新连接服务器...')
            await conn.reconnect()
            log.notice.info('重连服务器成功')
        } catch (error) {
            log.notice.error('重连服务器失败, 请检查网络: %s', error.message || '未知异常')
        }
    })
    opts.statusBarReconnect = statusBar
    opts.disposeReconnectCommand = disposable
    cache.constant.context.subscriptions.push(disposable)
    statusBar.text = format('%s-%s', opts.name || opts.host, '重连')
    statusBar.command = command
    statusBar.tooltip = format('%s\n重新连接到服务器', opts.host)
    statusBar.show()
}

function setShortcut(opts) {
    const name = opts.name || opts.host
    const target = 'hbuilderx://requestExtension/exampleid/'
    os.createShortcutToDesktop(name, target)
}

async function connect(options) {
    if (cache.connections.has(options.host)) {
        throw new Error(format('%s服务器已连接, 无需重复连接。', options.name || options.host))
    }
    const opts = _.omitBy(options, value => _.isNil(value) || value === '')
    switch (opts.method) {
        case 'key':
            delete opts.password
            break
        case 'pwd':
            delete opts.privateKey
            delete opts.privateKeyPath
            break
    }
    const conn = await ssh.connect(opts)
    conn.onClose = isUserClosed => {
        cache.dispatchViewsEvent(opts.host, 'onConnectionClosed', opts.host)
        if (isUserClosed) {
            return
        }
        setReconnect(opts)
        opts.stopForward()
    }
    conn.onOpen = () => {
        cache.dispatchViewsEvent(opts.host, 'onConnectionOpened', opts.host)
        opts.statusBarReconnect?.dispose()
        opts.disposeReconnectCommand?.dispose()
        opts.startForward()
    }
    const workerPath = join(__dirname, 'ssh-worker.js')
    const client = await requireObject(conn, 'Process', {
        scriptPath: workerPath,
        logPath: conn.logPath,
        metedata: {
            options: opts
        }
    })
    opts.startConnectTime = Date.now()
    if (opts.enableSave) {
        await save(opts)
    }
    opts.connection = client
    cache.connections.set(opts.host, opts)
    if (opts.enableFileExplorer) {
        setFileExplorer(opts)
    }
    if (opts.enableTerminal) {
        setShell(opts)
    }
    if (opts.enablePortForward) {
        setPortForward(opts)
    }
    if (opts.enableClose) {
        setClose(opts)
    }
    if (opts.enableShortcut) {
        setShortcut(opts)
    }
    if (opts.enableStats) {
        setStats(opts)
    }
    opts.disconnect = () => disconnect(opts.host)
    opts.startForward = async () => {
        const mappings = await cache.mappedPortTable.data(opts.host)
        if (mappings) {
            for (const it of mappings) {
                if (it.notConnected) {
                    continue
                }
                await forward(client, it)
                cache.dispatchViewsEvent(opts.host, 'onPortForwardChanged', {
                    host: it.host,
                    clientPort: it.clientPort,
                    notConnected: false
                })
            }
        }
    }
    opts.stopForward = async () => {
        cache.mappingPorts.forEach((value, key) => {
            if (key.startsWith(opts.host)) {
                if (value) {
                    value.child.kill('SIGINT')
                    cache.mappingPorts.delete(key)
                    cache.dispatchViewsEvent(opts.host, 'onPortForwardChanged', {
                        host: value.data.host,
                        clientPort: value.data.clientPort,
                        notConnected: true
                    })
                }
            }
        })
    }
    opts.startForward()
    return opts
}

async function disconnect(host, isConfirm = true) {
    const client = cache.connections.get(host)
    if (isConfirm) {
        const text = format(
            '确认关闭与以下服务器的连接吗?\n\n名称: %s\n地址: %s',
            client.name,
            client.host
        )
        const button = await hx.window.showMessageBox({
            type: 'question',
            title: '关闭服务器连接?',
            text: text,
            buttons: ['确认', '取消']
        })
        if (button != '确认') {
            return
        }
    }
    if (client) {
        client.connection.close()

        client.statusBarShell?.dispose()
        client.disposeShellCommand?.dispose()

        client.statusBarExplorer?.dispose()
        client.disposeExplorerCommand?.dispose()

        client.statusBarForward?.dispose()
        client.disposeForwardCommand?.dispose()

        client.statusBarClose?.dispose()
        client.disposeCloseCommand?.dispose()

        client.statusBarReconnect?.dispose()
        client.disposeReconnectCommand?.dispose()

        for (const key in cache.views) {
            const view = cache.views[key]
            if (view.contentId == host) {
                if (view.close()) {
                    delete cache.views[key]
                }
            }
        }
        client.stopForward()
        cache.connections.delete(host)
    }
    return host
}

module.exports = {
    connect,
    disconnect
}
